#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include "DemoDateTime.h"
#include "DemoArrayLoop.h"
#include "DemoFile.h"

void goThrougDir(char *dirPath) {
    if (0 == strlen(dirPath)) {
        printf("goThrougDir, empty arg dirPath\n");
        return;
    }
    
    // 打开目录操作
    DIR* dirp = opendir(dirPath);
    if (!dirp) {
        printf("goThrougDir, fail to open dirPath, %s\n", dirPath);
        return;
    }
    
// typedef struct {
//     int    __dd_fd;    /* file descriptor associated with directory */
//     long    __dd_loc;    /* offset in current buffer */
//     long    __dd_size;    /* amount of data returned */
//     char    *__dd_buf;    /* data buffer */
//     int    __dd_len;    /* size of data buffer */
//     long    __dd_seek;    /* magic cookie returned */
//     __unused long    __padding; /* (__dd_rewind space left for bincompat) */
//     int    __dd_flags;    /* flags for readdir */
//     __darwin_pthread_mutex_t __dd_lock; /* for thread locking */
//     struct _telldir *__dd_td; /* telldir position recording */
// } DIR;
    
    
    struct dirent * entry = NULL;//readdir(dirp);
//#define __DARWIN_STRUCT_DIRENTRY { \
//    __uint64_t  d_ino;      /* file number of entry */ \
//    __uint64_t  d_seekoff;  /* seek offset (optional, used by servers) */ \
//    __uint16_t  d_reclen;   /* length of this record */ \
//    __uint16_t  d_namlen;   /* length of string in d_name */ \
//    __uint8_t   d_type;     /* file type, see below */ \
//    char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */ \
//}
//
//struct dirent __DARWIN_STRUCT_DIRENTRY;

    
    //3,读取目录操作
    char pathBuf[1024] = {0};
    struct stat fileInfo;
    while ((entry = readdir(dirp)) != NULL) {
        sprintf(pathBuf, "%s/%s", dirPath, entry->d_name);
        if (0 == strcmp(entry->d_name, ".") || 0 == strcmp(entry->d_name, "..")) {
            // . 和 .. 跳过，不然死循环
            continue;
        }
        
        if (entry->d_type == DT_DIR) {
            printf("goThrougDir, type dir, %s\n", pathBuf);
            goThrougDir(pathBuf);
        } else {
            stat(pathBuf, &fileInfo);
//struct stat {
//    dev_t           st_dev;         /* [XSI] ID of device containing file */
//    ino_t           st_ino;         /* [XSI] File serial number */
//    mode_t          st_mode;        /* [XSI] Mode of file (see below) */
//    nlink_t         st_nlink;       /* [XSI] Number of hard links */
//    uid_t           st_uid;         /* [XSI] User ID of the file */
//    gid_t           st_gid;         /* [XSI] Group ID of the file */
//    dev_t           st_rdev;        /* [XSI] Device ID */
//#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
//    struct  timespec st_atimespec;  /* time of last access */
//    struct  timespec st_mtimespec;  /* time of last data modification */
//    struct  timespec st_ctimespec;  /* time of last status change */
//#else
//    time_t          st_atime;       /* [XSI] Time of last access */
//    long            st_atimensec;   /* nsec of last access */
//    time_t          st_mtime;       /* [XSI] Last data modification time */
//    long            st_mtimensec;   /* last data modification nsec */
//    time_t          st_ctime;       /* [XSI] Time of last status change */
//    long            st_ctimensec;   /* nsec of last status change */
//#endif
//    off_t           st_size;        /* [XSI] file size, in bytes */
//    blkcnt_t        st_blocks;      /* [XSI] blocks allocated for file */
//    blksize_t       st_blksize;     /* [XSI] optimal blocksize for I/O */
//    __uint32_t      st_flags;       /* user defined flags for file */
//    __uint32_t      st_gen;         /* file generation number */
//    __int32_t       st_lspare;      /* RESERVED: DO NOT USE! */
//    __int64_t       st_qspare[2];   /* RESERVED: DO NOT USE! */
//};
            printf("goThrougDir, type %d, size:%lld, %s\n", entry->d_type, fileInfo.st_size , pathBuf);
        }
    }
    closedir(dirp);
}

void txtFileWrite(void) {
    char *string = "水调歌头·明月几时有 \n\
苏轼 \n\
丙辰中秋，欢饮达旦，大醉，作此篇，兼怀子由。\n\
\n\
明月几时有？把酒问青天。\n\
不知天上宫阙，今夕是何年。\n\
我欲乘风归去，又恐琼楼玉宇，高处不胜寒。\n\
起舞弄清影，何似在人间。\n\
\n\
转朱阁，低绮户，照无眠。\n\
不应有恨，何事长向别时圆？\n\
人有悲欢离合，月有阴晴圆缺，此事古难全。\n\
但愿人长久，千里共婵娟。";
    
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    char writePath[1024] = {0};
    sprintf(writePath, "%s/Test.txt", cmdPath);
    
    printf("%s:%d, func %s\n", __FILE__, __LINE__, __func__);
    printf("cmdPath=%s\n", cmdPath);
    printf("writePath=%s\n",writePath);
    
//    r    打开一个已有的文本文件，允许读取文件。
//    w    打开一个文本文件，允许写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会从文件的开头写入内容。如果文件存在，则该会被截断为零长度，重新写入。
//    a    打开一个文本文件，以追加模式写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会在已有的文件内容中追加内容。
//    r+    打开一个文本文件，允许读写文件。
//    w+    打开一个文本文件，允许读写文件。如果文件已存在，则文件会被截断为零长度，如果文件不存在，则会创建一个新文件。
//    a+    打开一个文本文件，允许读写文件。如果文件不存在，则会创建一个新文件。读取会从文件的开头开始，写入则只能是追加模式
    FILE *fp = fopen(writePath, "w");
    if (!fp) {
        printf("fopen fail, writePath=%s\n",writePath);
        return;
    }
    
    // 把一个格式化字符串写入到文件
    int fprintfRes = fprintf(fp, "%s:%d, func %s\n\n", __FILE__, __LINE__, __func__);
    printf("fprintfRes=%d\n", fprintfRes);
    
    // 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功，它会返回一个非负值，如果发生错误，则会返回 EOF
    int putRes = fputs(string, fp);
    if (EOF == putRes) {
        printf("fputs fail, putRes=%d\n",putRes);
    } else {
        printf("fputs succ, putRes=%d\n",putRes);
    }
    
    // 把单个字符 s 写入到 fp
    for (int i=0;i<10;++i) {
        fputc('\n', fp);
    }
    
    fclose(fp);
}

void txtFileRead(void) {
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    char readPath[1024] = {0};
    sprintf(readPath, "%s/Test.txt", cmdPath);
    
    printf("%s:%d, func %s\n", __FILE__, __LINE__, __func__);
    printf("cmdPath=%s\n", cmdPath);
    printf("readPath=%s\n",readPath);
    
    //    r    打开一个已有的文本文件，允许读取文件。
    //    w    打开一个文本文件，允许写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会从文件的开头写入内容。如果文件存在，则该会被截断为零长度，重新写入。
    //    a    打开一个文本文件，以追加模式写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会在已有的文件内容中追加内容。
    //    r+    打开一个文本文件，允许读写文件。
    //    w+    打开一个文本文件，允许读写文件。如果文件已存在，则文件会被截断为零长度，如果文件不存在，则会创建一个新文件。
    //    a+    打开一个文本文件，允许读写文件。如果文件不存在，则会创建一个新文件。读取会从文件的开头开始，写入则只能是追加模式
    FILE *fp = fopen(readPath, "r");
    if (!fp) {
        printf("fopen fail, readPath=%s\n",readPath);
        return;
    }
    
//    fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符，如果发生错误则返回 EOF
    
    char readChar;
    int loop= 0;
    while((readChar = fgetc(fp)) != EOF) {
        printf("%c", readChar);
        ++ loop;
    }
    printf("\nfgetc单字符读取，调用次数 %d\n",loop);
    
    // 返回文件开头
    fseek(fp, 0, SEEK_SET);
    
//    fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf，并在最后追加一个 null 字符来终止字符串。
//    如果这个函数在读取最后一个字符之前就遇到一个换行符 '\n' 或文件的末尾 EOF，则只会返回读取到的字符，包括换行符
    char buff[128] = {0};
    loop= 0;
    while(fgets(buff, 128, fp)) {
        printf("%d: %s", loop, buff);
        ++ loop;
    }
    
    printf("\nfgets 按行读取，调用次数 %d\n",loop);
    
    fclose(fp);
}

size_t binWritePerson(FILE *fp, PersonData* person){
    size_t writeObjCount = 0;
    
    // name长度 + name字符串内容写入 二进制文件
    int strLen = (int)(strlen(person->name));
    writeObjCount += fwrite(&strLen, sizeof(int), 1, fp);
    writeObjCount += fwrite(person->name, sizeof(char), strLen, fp);
    
    // addr长度 + addr字符串内容写入 二进制文件
    strLen = (int)(strlen(person->addr));
    writeObjCount += fwrite(&strLen, sizeof(int), 1, fp);
    writeObjCount += fwrite(person->addr, sizeof(char), strLen, fp);
    
    // age写入 二进制文件
    writeObjCount += fwrite(&(person->age), sizeof(person->age), 1, fp);
    
    // gender写入 二进制文件
    writeObjCount += fwrite(&(person->gender), sizeof(person->gender), 1, fp);
    
    return writeObjCount;
}

size_t binReadPerson(FILE *fp, PersonData* person){
    size_t readCountTotal =0;
    size_t readCount = 0;
    
    // name长度
    int strLen = 0;
    // 成功读取的元素总数会以 size_t 对象返回，size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同，则可能发生了一个错误或者到达了文件末尾。
    readCount = fread(&strLen, sizeof(int), 1, fp);
    if (1 != readCount) {return -1;} else { readCountTotal += 1;}
    
    // name字符串内容
    readCount = fread(person->name, sizeof(char), strLen, fp);
    if (strLen != readCount) {return -1;} else { readCountTotal += 1;}
    
    // addr长度
    // 成功读取的元素总数会以 size_t 对象返回，size_t 对象是一个整型数据类型。如果总数与 nmemb 参数不同，则可能发生了一个错误或者到达了文件末尾。
    readCount = fread(&strLen, sizeof(int), 1, fp);
    if (1 != readCount) {return -1;} else { readCountTotal += 1;}
    
    // addr字符串内容
    readCount = fread(person->addr, sizeof(char), strLen, fp);
    if (strLen != readCount) {return -1;} else { readCountTotal += 1;}
    
    // age
    readCount = fread(&(person->age), sizeof(person->age), 1, fp);
    if (1 != readCount) {return -1;} else { readCountTotal += 1;}
    
    // age
    readCount = fread(&(person->gender), sizeof(person->gender), 1, fp);
    if (1 != readCount) {return -1;} else { readCountTotal += 1;}
    
    return readCountTotal;
}

void binFileWrite(void) {
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    char writePath[1024] = {0};
    sprintf(writePath, "%s/Test.bin", cmdPath);
    
    printf("%s:%d, func %s\n", __FILE__, __LINE__, __func__);
    printf("cmdPath=%s\n", cmdPath);
    printf("writePath=%s\n",writePath);
    
    //    r    打开一个已有的文本文件，允许读取文件。
    //    w    打开一个文本文件，允许写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会从文件的开头写入内容。如果文件存在，则该会被截断为零长度，重新写入。
    //    a    打开一个文本文件，以追加模式写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会在已有的文件内容中追加内容。
    //    r+    打开一个文本文件，允许读写文件。
    //    w+    打开一个文本文件，允许读写文件。如果文件已存在，则文件会被截断为零长度，如果文件不存在，则会创建一个新文件。
    //    a+    打开一个文本文件，允许读写文件。如果文件不存在，则会创建一个新文件。读取会从文件的开头开始，写入则只能是追加模式
    //    如果处理的是二进制文件，则需使用下面的访问模式来取代上面的访问模式：
    //    "rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
    FILE *fp = fopen(writePath, "wb");
    if (!fp) {
        printf("fopen fail, readPath=%s\n",writePath);
        return;
    }
    
    PersonData* p1 = makePerson("LiLei", "class-1-1", 12, 1);
    PersonData* p2 = makePerson("HanMeiMei", "class-1-2", 13, 0);
    PersonData* p3 = makePerson("JimGreen__longtest__longtest__longtest__longtest__longtest__longtest__longtest",
                                "class-1-3__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest__longtest",
                                11, 1);
    
    int writeObjCount = 0;
    // 在文件开头写入数量
    int personCount = 3;
    // 该函数返回一个 size_t 对象，表示元素的总数
    writeObjCount += fwrite(&personCount, sizeof(int), 1, fp);
    printf("binWrite personCount, writeObjCount=%d, ftell=%ld\n\n",writeObjCount, ftell(fp));
    
    printPersonInfo(p1);
    writeObjCount += binWritePerson(fp, p1);
    printf("binWritePerson p1, writeObjCount=%d, ftell=%ld\n\n",writeObjCount, ftell(fp));
    
    printPersonInfo(p2);
    writeObjCount += binWritePerson(fp, p2);
    printf("binWritePerson p2, writeObjCount=%d, ftell=%ld\n\n",writeObjCount, ftell(fp));
    
    printPersonInfo(p3);
    writeObjCount += binWritePerson(fp, p3);
    printf("binWritePerson p3, writeObjCount=%d, ftell=%ld\n\n",writeObjCount, ftell(fp));
    
    fclose(fp);
}

void binFileRead(void) {
    // 获取到可执行程序的绝对路径
    char cmdPath[1024] = {0};
    getcwd(cmdPath, 1024);
    
    char readPath[1024] = {0};
    sprintf(readPath, "%s/Test.bin", cmdPath);
    
    printf("%s:%d, func %s\n", __FILE__, __LINE__, __func__);
    printf("cmdPath=%s\n", cmdPath);
    printf("readPath=%s\n",readPath);
    
    //    r    打开一个已有的文本文件，允许读取文件。
    //    w    打开一个文本文件，允许写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会从文件的开头写入内容。如果文件存在，则该会被截断为零长度，重新写入。
    //    a    打开一个文本文件，以追加模式写入文件。如果文件不存在，则会创建一个新文件。在这里，您的程序会在已有的文件内容中追加内容。
    //    r+    打开一个文本文件，允许读写文件。
    //    w+    打开一个文本文件，允许读写文件。如果文件已存在，则文件会被截断为零长度，如果文件不存在，则会创建一个新文件。
    //    a+    打开一个文本文件，允许读写文件。如果文件不存在，则会创建一个新文件。读取会从文件的开头开始，写入则只能是追加模式
    //    如果处理的是二进制文件，则需使用下面的访问模式来取代上面的访问模式：
    //    "rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
    FILE *fp = fopen(readPath, "rb");
    if (!fp) {
        printf("fopen fail, readPath=%s\n",readPath);
        return;
    }
    
    int personCount = 0;
    size_t readCountTotal = 0;
    
    size_t readCount = fread(&personCount, sizeof(int), 1, fp);
    if (1 != readCount) {printf("读取personCount 失败");return;} else { readCountTotal += 1;}
    
    for (int i=0; i<personCount; ++i) {
        PersonData* person = (PersonData*)malloc(sizeof(PersonData));
        
        readCount = binReadPerson(fp, person);
        if (-1 == readCount) {printf("读取person[%d] 失败", i);return;} else { readCountTotal += 1;}
        
        printf("\nbinFileRead, person[%d], readCountTotal=%ld, ftell=%ld\n", i, readCountTotal, ftell(fp));
        printPersonInfo(person);
        
    }
    
    printf ("\nuse %s\n", isLittleEndian() ? "Little-Endian" : "Big-Endian");
    // 返回文件开头
    fseek(fp, 0, SEEK_SET);
    char byteRead = 0;
    for(int i=0;i<sizeof(int);++i) {
        fread(&byteRead, sizeof(char), 1, fp);
        printf("personCount %#0.8X, byte %d, %#0.2X\n", personCount, i, byteRead);
    }
    
    
    fclose(fp);
}



void demoFileLogic(void) {
    logWsBegin("demoFileLogic begin\n");
    
    goThrougDir("/etc");
    
    txtFileWrite();
    txtFileRead();
    
    binFileWrite();
    binFileRead();
    
    logWs("demoFileLogic end\n");
}
